package cn.chair.interceptor;

import cn.chair.annotation.PublicApi;
import cn.chair.dao.AppDao;
import cn.chair.util.SmallUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import life.innovation.exception.FailedDependencyException;
import life.innovation.exception.InvalidInputException;
import life.innovation.exception.UnauthenticatedException;
import life.innovation.exception.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class SecurityInterceptor extends HandlerInterceptorAdapter {
	private static final Logger log = LoggerFactory.getLogger("appLogger");

	public static final String KEY_TOKEN = "token";

	@Autowired @Lazy
	private RedisTemplate<String, String> redisTemplate;

	@Value("${jwt.private.key}")
	private String jwtKey;

	@Autowired @Lazy
	private AppDao appDao;

	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {

		if (isPublicApi(handler)) {
			return true;
		}
		String uri = request.getRequestURI();
		if(containUri(uri, getLoginUri())) {
			return true;
		}

		String token = getToken(request);

        Map<String,Object> claims = parseToken(token);
		if(claims == null){
			throw new UnauthorizedException();
		}

		return true;
   }

	private boolean isPublicApi(Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        PublicApi annotation = handlerMethod.getMethodAnnotation(PublicApi.class);

        return annotation != null;
    }
	private String getToken(HttpServletRequest request) throws UnauthenticatedException, FailedDependencyException
	{
		String token = request.getHeader("token");

		if (token == null) {
			throw new UnauthenticatedException("error.token.missing");
		}

		return token;

	}

	private Map<String, Object> parseToken(String token) {

		Claims claims;
		try {
			claims = Jwts.parser()
					.setSigningKey(jwtKey)
					.parseClaimsJws(token)
					.getBody();
		} catch (Exception e) {
			log.debug(e.toString());
			claims = null;
		}

		return claims;

	}

	private boolean containUri(String uri, List<String> patterns) {
		for (String pattern : patterns) {
			Pattern p = Pattern.compile(pattern);
			Matcher m = p.matcher(uri);
			
			if (m.find()) {
				return true;
			}
		}
		return false;
	}
	// 登录无需验证token
	private List<String> getLoginUri()
	{
		List<String> list = new ArrayList<> ();
		list.add("^/login");
		return list;
	}

    // TODO: should be replaced by PublicApi annotation
	private List<String> getExemptUriList() {
		List<String> list = new ArrayList<> ();
		list.add("^/$");
		list.add("^/test");
        list.add("^/error$");
        list.add("^/cmbc/payments/callback$");
		list.add("^/admin/configs/register$");
        list.add("^/notify");

        list.add("^/orders/\\w+/payments");
        list.add("^/payments/\\w+");
        list.add("^/admin/reconciliations");
        list.add("^/admin/trans/\\w+/reversals");
		list.add("^/login");
		list.add("^/claimsFromToken");
		list.add("^/order");
		list.add("^/pay");
		list.add("^/pclist");
		list.add("^/addMerchant");
		list.add("^/modifyMerchant");
		list.add("^/queryChair");
		list.add("^/delChair");
		list.add("^/modifyChair");
		list.add("^/addChair");
		list.add("^/chairs");
		list.add("^/pricesList");
		list.add("^/delPrices");
		list.add("^/modifyPrices");
		list.add("^/addPrices");
		return list;
	}
}
